package core

import (
	"hash"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/common/math"
)

// keccakState wraps sha3.state. In addition to the usual hash methods, it also supports
// Read to get a variable amount of data from the hash state. Read is faster than Sum
// because it doesn't copy the internal state, but also modifies the internal state.
type keccakState interface {
	hash.Hash
	Read([]byte) (int, error)
}

type EVMInterpreter struct {
	evm *BatchEvm

	instructionTable *InstructionTable

	//这两个数据结构只和KECCAK256 OpCode有关
	hasher    keccakState
	hasherBuf common.Hash

	readOnly   bool
	returnData []byte
}

func (inter *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ([]byte, error) {
	inter.evm.Depth++
	defer func() { inter.evm.Depth-- }()

	if readOnly && !inter.readOnly {
		inter.readOnly = true
		defer func() { inter.readOnly = false }()
	}

	inter.returnData = nil

	if len(contract.Code) == 0 {
		return nil, nil
	}

	//初始化运行时候所需要的状态
	var (
		op          OpCode // Current Code
		mem         = NewMemory()
		stack       = NewStack()
		callContext = &ScopeContext{
			Memory:       mem,
			Stack:        stack,
			CallContract: contract,
		}

		pc   = uint64(0) // Program Counter
		cost uint64
		res  []byte
		err  error
	)

	contract.Input = input

	for {
		//获取Op
		op = callContext.CallContract.GetOp(pc)
		operation := inter.instructionTable[op]

		//检查栈是否溢出
		if sLen := stack.Len(); sLen < operation.minStack {
			return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack}
		} else if sLen > operation.maxStack {
			return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
		}

		//分析扣除指令的静态Gas开销
		cost = operation.constantGas
		if !contract.UseGas(cost) {
			return nil, ErrOutOfGas
		}

		//分析扣除指令的动态Gas开销（如果需要的话）
		if operation.dynamicGas != nil {
			var memorySize uint64
			if operation.memorySize != nil {
				memSize, overflow := operation.memorySize(stack)
				if overflow {
					return nil, ErrGasUintOverflow
				}
				if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
					return nil, ErrGasUintOverflow
				}
			}
			//计算dynamicGas开销，检测剩余的Gas是否充足
			var dynamicGas uint64
			dynamicGas, err = operation.dynamicGas(inter.evm, contract, stack, mem, memorySize)
			if err != nil || !contract.UseGas(dynamicGas) {
				return nil, ErrOutOfGas
			}
			if memorySize > 0 {
				mem.Resize(memorySize)
			}
		}

		//扣除完成之后执行该指令
		res, err = operation.execute(&pc, inter, callContext)
		if err != nil {
			if err == errStopToken {
				err = nil
			}
			break
		}
		pc++
	}
	return res, err
}
